﻿/**
 * ...
	 * @author Herbs
	 * Provides common methods for sending data to and receiving from a resource identified by URL string
	 */
package net 
{
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.display.Loader;
	import flash.display.MovieClip;
	import flash.events.Event;
	import flash.events.IOErrorEvent;
	import flash.events.ProgressEvent;
	import flash.net.URLLoader;
	import flash.net.URLLoaderDataFormat;
	import flash.net.URLRequest;
	
	import flash.system.JPEGLoaderContext;
	import flash.system.LoaderContext;
	import flash.utils.ByteArray;
	

	public class WebClient 
	{
		public static const IMAGE:String = "image";
		public static const STRING:String = "string";
		public static const BINARY:String = "binary";
		public static const TXML:String = "txml";
		public static const BITMAP:String = "bitmap";
		public static const MOVIECLIP:String = "movieclip";
		
		public var fnDownloadComplete:Function;
		public var fnDownloadError:Function;
		public var fnDownloadTimeOut:Function;
		public var fnDownloadProgress:Function;
		private static var wcUniqueInstance:WebClient;
		public var params:Array = [];
		
		private var urlLoader:URLLoader;
		private var displayLoader:Loader;
		private var fnTranslateData:Function;
		private var bLoading:Boolean = false;
		private var bChildisSwf:Boolean = false;
		
		/**
		 * Initializes a new instance of the com.sailorless.net.WebClient class
		 */
		public function WebClient() {}
		
		
		/**
		 * Get the global unique instance.
		 */
		public static function get uniqueClient():WebClient
		{
			if (wcUniqueInstance == null)
				wcUniqueInstance = new WebClient();
			return wcUniqueInstance;
		}
			
		
		/**
		 * Close the load operation in progress.
		 */
		public function close():void 
		{
			
			if (bLoading)
			{
				if (urlLoader != null)
				{
					urlLoader.removeEventListener(Event.COMPLETE, onLoadComplete);
					urlLoader.removeEventListener(IOErrorEvent.IO_ERROR, onIOError);
					urlLoader.removeEventListener(ProgressEvent.PROGRESS, onProgress);
					urlLoader.close();
					urlLoader = null;
				}
				else if (displayLoader != null)
				{
					displayLoader.contentLoaderInfo.removeEventListener(Event.COMPLETE, onLoadComplete2);
					displayLoader.contentLoaderInfo.removeEventListener(IOErrorEvent.IO_ERROR, onIOError);
					displayLoader.contentLoaderInfo.removeEventListener(ProgressEvent.PROGRESS, onProgress);
					if (bChildisSwf)
						displayLoader.unloadAndStop(true);
					else 
						displayLoader.unload();
					displayLoader = null;
				}
			}
			bChildisSwf = false;
			bLoading = false;
		}
		
		/**
		 * Downloads a swf file from a resource and call the completed handler with MovieClip.
		 * @param	addr	the resource address
		 */
		public function downloadSWF(addr:String):void 
		{
			if (bLoading) close();
			
			bChildisSwf = true;
			bLoading = true;
			fnTranslateData = translateToMovieClip;
			displayLoader = new Loader();
			displayLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, onLoadComplete2);
			displayLoader.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, onIOError);
			displayLoader.contentLoaderInfo.addEventListener(ProgressEvent.PROGRESS, onProgress);
			displayLoader.load(new URLRequest(addr));
		}

		/**
		 * Downloads a png/jpg file from a resource and call the completed handler with Bitmapdata.
		 * @param	addr	the resource address
		 * @param	async   decode the image data when it is loaded if the async is true
		 */
		public function downloadImage(addr:String, async:Boolean = false):void 
		{
			if (bLoading) close();

			bLoading = true;
			fnTranslateData = translateToBitmap;
			displayLoader = new Loader();
			displayLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, onLoadComplete2);
			displayLoader.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, onIOError);
			displayLoader.contentLoaderInfo.addEventListener(ProgressEvent.PROGRESS, onProgress);
			var context:LoaderContext = new LoaderContext();
			//if (async)
				//context.imageDecodingPolicy = ImageDecodingPolicy.ON_LOAD;
			displayLoader.load(new URLRequest(addr), context);
		}
	
		/**
		 * Downloads data from a resource and call the completed handler with Bytearray.
		 * @param	addr	the resource address
		 */
		public function downloadData(addr:String):void 
		{
			if (bLoading)	close();
			
			bLoading = true;
			fnTranslateData = translateToBytes;
			urlLoader = new URLLoader();
			urlLoader.dataFormat = URLLoaderDataFormat.BINARY;
			urlLoader.addEventListener(Event.COMPLETE, onLoadComplete);
			urlLoader.addEventListener(IOErrorEvent.IO_ERROR, onIOError);
			urlLoader.addEventListener(ProgressEvent.PROGRESS, onProgress);
			urlLoader.load(new URLRequest(addr));
		}
		
		/**
		 * Downloads a String from a resource and call the completed handler with a String.
		 * @param	url		the resource address
		 */
		public function downloadString(addr:String):void 
		{
			if (bLoading)	close();
			
			bLoading = true;
			fnTranslateData = translateToString;
			urlLoader = new URLLoader();
			urlLoader.addEventListener(Event.COMPLETE, onLoadComplete);
			urlLoader.addEventListener(IOErrorEvent.IO_ERROR, onIOError);
			urlLoader.addEventListener(ProgressEvent.PROGRESS, onProgress);
			urlLoader.load(new URLRequest(addr));
		}
		
		/**
		 * Downloads a XML format string from a resource and call the completed handler with a XML.
		 * @param	url		the resource address
		 */
		public function downloadXML(addr:String):void 
		{
			if (bLoading)	close();
			
			bLoading = true;
			fnTranslateData = translateToXML;
			urlLoader = new URLLoader();
			urlLoader.addEventListener(Event.COMPLETE, onLoadComplete);
			urlLoader.addEventListener(IOErrorEvent.IO_ERROR, onIOError);
			urlLoader.addEventListener(ProgressEvent.PROGRESS, onProgress);
			urlLoader.load(new URLRequest(addr));
		}
		
		
//==================================================================================================================================//
		/**
		 * Convert the data to a String.
		 * @param	data
		 * @return
		 */
		private function translateToString(data:*):String
		{
			return String(data);
		}
		
		/**
		 * Convert the data to XML.
		 * @param	data
		 * @return
		 */
		private function translateToXML(data:*):XML
		{
			return new XML(data);
		}
		
		private function translateToBytes(data:*):ByteArray
		{
			return ByteArray(data);
		}
		
		private function translateToBitmap(data:*):Bitmap
		{	
			return Bitmap(data);
		}
		
		private function translateToMovieClip(data:*):MovieClip
		{
			return MovieClip(data);
		}
		
		

//==================================================================================================================================//
		//LoadComplete handler
		private function onLoadComplete(e:Event):void 
		{
			bLoading = false;
			urlLoader.removeEventListener(Event.COMPLETE, onLoadComplete);
			urlLoader.removeEventListener(IOErrorEvent.IO_ERROR, onIOError);
			urlLoader.removeEventListener(ProgressEvent.PROGRESS, onProgress);
		
			
			var data:* = urlLoader.data;
			urlLoader = null;
			if (fnDownloadComplete != null)
			{
				params.unshift(fnTranslateData(data))
				fnDownloadComplete.apply(null, params);
			}
		
		}
		
		private function onLoadComplete2(e:Event):void 
		{
			bLoading = false;
			displayLoader.contentLoaderInfo.removeEventListener(Event.COMPLETE, onLoadComplete);
			displayLoader.contentLoaderInfo.removeEventListener(IOErrorEvent.IO_ERROR, onIOError);
			displayLoader.contentLoaderInfo.removeEventListener(ProgressEvent.PROGRESS, onProgress);
			var data:* = displayLoader.content;
			displayLoader = null;
			bChildisSwf = false;
			
			if (fnDownloadComplete != null)
			{
			
				params.unshift(fnTranslateData(data));
				
				fnDownloadComplete.apply(null, params);
			}
			
		}
		
		//IOError handler
		private function onIOError(e:IOErrorEvent):void 
		{
			if (fnDownloadError != null)
				fnDownloadError.apply(null, params);
			else
				throw new Error(e.text);
		}
		
		//Progress handler
		private function onProgress(e:ProgressEvent):void 
		{
			if (fnDownloadProgress != null)
				fnDownloadProgress.apply(null, [e.bytesLoaded, e.bytesTotal]);
		}
		
	}

}